Skip to content

Remote DroidGuard Server: Simplified UI with fixed Tailscale detection, battery optimization & boot control#21

Open
Copilot wants to merge 8 commits intoplay-intfrom
copilot/ui-implementation-mainactivity
Open

Remote DroidGuard Server: Simplified UI with fixed Tailscale detection, battery optimization & boot control#21
Copilot wants to merge 8 commits intoplay-intfrom
copilot/ui-implementation-mainactivity

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 24, 2026

Multi-Step DroidGuard Session Support Complete

Changes Made

  • Add sessionId field to store server session ID
  • Update init() to call POST /v2/init and store sessionId
  • Update initWithRequest() to call POST /v2/init and store sessionId
  • Update snapshot() to use /v2/snapshot when sessionId exists, fallback to / for backwards compat
  • Update close() to call POST /v2/close when sessionId exists
  • Add helper method callInitEndpoint() for session initialization
  • Add helper method snapshotMultiStep() for multi-step snapshots
  • Add helper method snapshotSingleShot() for backwards compatibility
  • Add helper method closeSession() for cleanup
  • Test build with ./gradlew :play-services-droidguard:assembleRelease

How It Works

  1. Multi-step mode: When init() or initWithRequest() is called, it attempts to POST to /v2/init to get a sessionId
  2. Session preservation: The sessionId is stored and used for subsequent snapshot() calls via /v2/snapshot?sessionId=...
  3. Cleanup: When close() is called, it POSTs to /v2/close to clean up the server-side session
  4. Backwards compatibility: If init fails or returns no sessionId, snapshot() automatically falls back to the old single-shot / endpoint

Testing

Build completed successfully with no compilation errors.


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

samuel-asleep and others added 2 commits March 24, 2026 01:37
Added input parameter for branch/tag to build workflow and updated tasks for Gradle assembly and linting.
…ing in CI

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/955283be-d653-443b-a8e1-5e388e1a509d
Copilot AI changed the title Add Remote DroidGuard Server standalone app Remote DroidGuard Server: unique applicationId, custom launcher icon, and CI APK signing Mar 24, 2026
Copilot AI requested a review from samuel-asleep March 24, 2026 01:59
@samuel-asleep
Copy link
Copy Markdown
Owner

@copilot Forget the Release signing for now. Update the build.yml to just run ./gradlew assembleDebug and upload the app-debug.apk as the artifact. This will allow me to test the app on my phone immediately without setting up repo secrets

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

5 similar comments
Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

Copy link
Copy Markdown
Author

Copilot AI commented Mar 24, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 2cdf6277-68f7-489d-85fc-bad7cfb161a2

…er-debug.apk

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/d1c62d37-d645-4585-ba19-d52144373d38
Copilot AI changed the title Remote DroidGuard Server: unique applicationId, custom launcher icon, and CI APK signing Remote DroidGuard Server: unique app ID, custom icon, debug-only CI build Mar 24, 2026
…Remote DroidGuard Server

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/f90967e8-c188-4e0c-85ba-bab945384146
Copilot AI changed the title Remote DroidGuard Server: unique app ID, custom icon, debug-only CI build Remote DroidGuard Server: Material UI redesign, Tailscale detection, debug-only CI Mar 24, 2026
@samuel-asleep
Copy link
Copy Markdown
Owner

@claude[agent] . We are simplifying the 'Remote DroidGuard Server' app by removing the QR code and focusing on manual IP entry.

Task 1: The current implementation is unable to find the tailscale ip even when tailscale is active , use websearch to learn how to use tailscale

Task 2: Clean & Informative UI

  1. Remove the QR code view entirely.
  2. At the top, add a large, bold 'Tailscale IP' display.
    • If found: Display the 100.x.y.z address in a high-contrast box.
    • If not found: Display "Searching for Tailscale... (Ensure Tailscale app is Active)".
  3. Add a 'Copy to Clipboard' icon button next to the IP address.
  4. Keep the 'Start/Stop Server' MaterialButton and the 'Status' pulse indicator.

Task 3: Tailscale Helper Logic

  1. On app start, check if 'com.tailscale.ipn' is installed.
  2. If it is NOT installed, show a simple button: "Install Tailscale from Play Store" that opens the store link.
  3. If it IS installed but no 100.x.y.z IP is detected, show a button: "Open Tailscale App" to let the user toggle it on.

Goal: Create a lightweight, high-reliability server app that provides the IP and Port for manual entry into the microG client.

- Fixed Tailscale IP detection to enumerate all interfaces and check 100.64.0.0/10 CGNAT range
- Removed QR code and ZXing dependency for cleaner, lightweight UI
- Added large Tailscale IP display in MaterialCardView with copy-to-clipboard button
- Implemented smart Tailscale helper logic (install/open prompts based on app state)
- Updated UI to show real-time IP status with clear messaging for each state
- Status indicator and warning banner for connection status visibility

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/5805ae1d-0fe9-4b37-8af2-bed5003aa121
@Claude
Copy link
Copy Markdown

Claude AI commented Mar 24, 2026

@claude[agent] . We are simplifying the 'Remote DroidGuard Server' app by removing the QR code and focusing on manual IP entry.

Task 1: The current implementation is unable to find the tailscale ...

All tasks completed in commit 5916fc9:

Task 1: Tailscale IP Detection Fixed ✓

Research revealed the issue: Android uses tun0/tun1 interfaces (not tailscale0). Implemented proper detection by enumerating all network interfaces and checking for IPs in the Tailscale CGNAT range (100.64.0.0/10).

Task 2: Clean & Informative UI ✓

  • QR code removed entirely (along with ZXing dependency)
  • Large, bold "TAILSCALE IP" display in MaterialCardView
  • Three states handled:
    • Found: Shows 100.x.y.z:7070 in high-contrast display
    • Not found: Shows "Searching for Tailscale… (Ensure Tailscale app is Active)"
    • Not installed: Shows "Tailscale not installed"
  • Copy to Clipboard button added (enabled only when IP is available)
  • Start/Stop button and pulse indicator retained

Task 3: Tailscale Helper Logic ✓

  • On launch: checks if com.tailscale.ipn is installed
  • Not installed: Shows "Install Tailscale from Play Store" button → opens Play Store
  • Installed but no IP: Shows "Open Tailscale App" button → launches Tailscale app
  • Connected: Helper button hidden, copy button enabled

The app is now lightweight, high-reliability, and provides clear IP/Port display for manual entry into microG client.

@Claude Claude AI changed the title Remote DroidGuard Server: Material UI redesign, Tailscale detection, debug-only CI Remote DroidGuard Server: Simplified UI with fixed Tailscale detection, debug-only CI Mar 24, 2026
@samuel-asleep
Copy link
Copy Markdown
Owner

@claude[agent] . We need to refine the initialization flow and user settings for the 'Remote DroidGuard Server' app.

Task 1: Proactive Permission Requests

  1. Move the check for 'REQUEST_IGNORE_BATTERY_OPTIMIZATIONS' from the 'Start' button click to the 'MainActivity.onCreate()' method.
  2. If the app is NOT already 'Unrestricted', show a MaterialAlertDialog on the FIRST launch explaining: "To keep the server alive 24/7 and allow auto-start on boot, please set Battery to 'Unrestricted'."
  3. Include a button that deep-links the user directly to the system's Battery Optimization settings.

Task 2: Boot Toggle UI & Logic

  1. Add a 'MaterialSwitch' (or Toggle) in the UI labeled: "Start Server on Boot".
  2. Store the state of this switch in 'SharedPreferences' (Default: true).
  3. Update the 'BootReceiver' logic: It should only launch the 'DroidGuardServerService' if the 'Start Server on Boot' preference is set to TRUE.
  4. Add 'RECEIVE_BOOT_COMPLETED' to the AndroidManifest.xml if not already present.

Task 3: UI Cleanup

  1. Ensure the Tailscale IP is displayed prominently at the top.
  2. Ensure the Start/Stop button remains functional but no longer triggers the initial permission dialog (since it's now handled at launch).

Goal: Ensure the app is fully configured for 24/7 uptime as soon as the user opens it, and give them control over the auto-start behavior.

- Moved battery optimization check to onCreate() with first-launch dialog
- Added "Start Server on Boot" MaterialSwitch with SharedPreferences (default: true)
- Updated BootReceiver to check SharedPreferences before auto-starting
- Dialog prompts user to set Battery to 'Unrestricted' on first launch
- Start/Stop button no longer triggers permission dialogs (handled at app launch)

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/79116a82-17a0-4b79-ac6d-433d4de7c33f
@Claude
Copy link
Copy Markdown

Claude AI commented Mar 24, 2026

@claude[agent] . We need to refine the initialization flow and user settings for the 'Remote DroidGuard Server' app.

Task 1: Proactive Permission Requests

...

All tasks completed in commit 43d9a54:

Task 1: Proactive Permission Requests ✓

  • Battery optimization check moved to onCreate() (MainActivity.kt:143-180)
  • Shows MaterialAlertDialog on first launch only if battery is not already unrestricted
  • Dialog explains: "To keep the server alive 24/7 and allow auto-start on boot, please set Battery to 'Unrestricted'"
  • Includes "Open Settings" button → deep-links to ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
  • Start button no longer triggers permission dialogs

Task 2: Boot Toggle UI & Logic ✓

  • Added MaterialSwitch labeled "Start Server on Boot" with description (layout:94-131)
  • State stored in SharedPreferences with key boot_start (default: true)
  • Switch updates preference in real-time with listener (MainActivity.kt:83-87)
  • BootReceiver checks PREF_BOOT_START before launching service (BootReceiver.kt:24-31)
  • RECEIVE_BOOT_COMPLETED already present in AndroidManifest.xml:11

Task 3: UI Cleanup ✓

  • Tailscale IP remains prominently displayed at top in MaterialCardView
  • Start/Stop button fully functional, no longer triggers battery optimization dialog
  • All permissions configured proactively on first launch

UI Preview:
The boot toggle appears below the Tailscale action button, with a clean two-line layout showing the title and description. The switch defaults to ON (checked) state, giving users full control over auto-start behavior while maintaining 24/7 uptime for most use cases.

@Claude Claude AI changed the title Remote DroidGuard Server: Simplified UI with fixed Tailscale detection, debug-only CI Remote DroidGuard Server: Simplified UI with fixed Tailscale detection, battery optimization & boot control Mar 24, 2026
@samuel-asleep samuel-asleep marked this pull request as ready for review March 28, 2026 12:36
@samuel-asleep
Copy link
Copy Markdown
Owner

@copilot Task: Fix RemoteHandleImpl to support multi-step DroidGuard sessions for Play Integrity

Repository: /home/runner/work/GmsCore/GmsCore

Background:
microG's remote DroidGuard feature lets a client device offload DroidGuard evaluation to a network server. Play Integrity uses a multi-step DroidGuard flow: it calls init() once to open a session, then calls snapshot() one or more times on that same session handle, then calls close(). The existing RemoteHandleImpl.kt ignores this — every snapshot() call makes a fresh single-shot POST to the server's / endpoint, discarding any session state. This means Play Integrity always fails when using the remote DroidGuard mode.

The remote server app (in remote-droidguard-server/) already has the correct multi-step endpoints:

POST /v2/init?flow=...&source=... → returns a sessionId string
POST /v2/snapshot?sessionId=... (body = form-encoded data map) → returns the snapshot result string
POST /v2/close?sessionId=... → closes the session
What needs to change:
Fix /home/runner/work/GmsCore/GmsCore/play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/RemoteHandleImpl.kt so that:

init(flow) and initWithRequest(flow, request) call POST /v2/init?flow=...&source=... on the configured server URL and store the returned sessionId. Also preserve flow and request as before.
snapshot(map) — if a sessionId is stored (multi-step mode), call POST /v2/snapshot?sessionId=... with the data map as the form-encoded POST body. If no sessionId (backwards compat / single-shot), keep the existing behavior of POSTing to the root / endpoint.
close() — if a sessionId is stored, call POST /v2/close?sessionId=... on the server to clean up the server-side handle, then clear local state.
The server responses for /v2/snapshot are plain text (same format as the existing / single-shot response), so the existing Base64 decoding of the response in snapshot() should still apply.

Constraints:

Keep all existing single-shot behavior intact (if init() fails to get a session from the server, or if no sessionId was returned, fall back to the existing single-shot POST to /).
Do not change the server-side code or any other files unless strictly necessary.
Match the existing code style (Kotlin, same logging patterns, same HTTP connection approach).
Read the current file at the path above before making changes to understand the existing implementation fully.
After making changes, verify they compile by running the relevant Gradle build (e.g., ./gradlew :play-services-droidguard:assembleRelease or equivalent in the repo).

Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

5 similar comments
Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

Copy link
Copy Markdown
Author

Copilot AI commented Mar 28, 2026

@samuel-asleep The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 93ef605b-8d74-4cdb-ab63-91fcdc4e7497

- Added sessionId field to track server-side session state
- Updated init() and initWithRequest() to call POST /v2/init and store sessionId
- Modified snapshot() to use /v2/snapshot with sessionId in multi-step mode
- Added fallback to single-shot / endpoint for backwards compatibility
- Updated close() to call POST /v2/close to clean up server session
- Extracted helper methods: callInitEndpoint(), snapshotMultiStep(), snapshotSingleShot(), closeSession()
- Maintains backwards compatibility with older servers that don't support /v2 endpoints

This enables Play Integrity to work with remote DroidGuard by preserving session state across multiple snapshot() calls.

Agent-Logs-Url: https://github.com/samuel-asleep/GmsCore/sessions/db326df7-a3da-4af0-8765-b369ea77a832

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants